package com.demo14;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.AssetManager;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.utils.SearchAesKey;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

public class SimpleSign extends AbstractJni implements IOResolver {
    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        System.out.println("file open:"+pathname);
        if(pathname.contains("status")){
            return FileResult.success(new ByteArrayFileIO(oflags, pathname, "TracerPid:      0".getBytes(StandardCharsets.UTF_8)));
        }
        if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {
            return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view\0".getBytes()));
        }
        if(pathname.equals("/data/app/ctrip.android.view-fM4xyjk_eygpJsiITNW4JA==/base.apk")){
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/demo14/base.apk"), pathname));
        }
        return null;
    }

    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    public DvmClass SecurityUtil;

    SimpleSign() {
        emulator = AndroidEmulatorBuilder
                .for32Bit()
                .setProcessName("com.ximalaya.ting.android")
                .addBackendFactory(new Unicorn2Factory(true))
                .setRootDir(new File("unidbg-android/src/test/resources/demo14/rootfs"))
                .build();
        Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析

        emulator.getSyscallHandler().addIOResolver(this);
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        emulator.getBackend().registerEmuCountHook(100000); // 设置执行多少条指令切换一次线程

        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/demo14/base.apk")); // 创建Android虚拟机

        new AndroidModule(emulator, vm).register(memory);
        DalvikModule dm = vm.loadLibrary("scmain", true);
        module = dm.getModule();

        vm.setJni(this);
        vm.setVerbose(true);

        dm.callJNI_OnLoad(emulator);
        SecurityUtil = vm.resolveClass("ctrip/android/security/SecurityUtil");

//        ArrayList<String> funclist = SearchAesKey.readFuncFromIDA("unidbg-android/src/test/resources/demo14/functions/libscmain_functionlist_1666569352.txt");
//        SearchAesKey aesKeyFinder = new SearchAesKey(emulator);
//        aesKeyFinder.searchEveryFunction(module.base, funclist);
    }


    public void init(){
        DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);// context
        SecurityUtil.newObject(null).callJniMethod(emulator, "init(Landroid/content/Context;)V", context);
    }

    public void callSimpleSign(){
        String result = SecurityUtil.newObject(null).callJniMethodObject(emulator, "simpleSign([BLjava/lang/String;)Ljava/lang/String;", "hello".getBytes(StandardCharsets.UTF_8), "getdata").getValue().toString();
        System.out.println("simpleSIgn:"+result);
    }

    public static void main(String[] args) {
        SimpleSign simpleSign = new SimpleSign();
        simpleSign.init();
        simpleSign.callSimpleSign();
    }

    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;":{
                return vm.resolveClass("java/io/File").newObject("/mnt/sdcard");
            }
            case "okio/zz->b(I)Ljava/lang/String;":{
                int key = varArg.getIntArg(0);
                switch (key){
                    case 1:{
                        return new StringObject(vm, "353626076466627");
                    }
                    case 0:{
                        return new StringObject(vm, "8cff8823cf19b5ec");
                    }
                    case 101:{
                        return new StringObject(vm, "25483");
                    }
                    case 103:{
                        return new StringObject(vm, "1920*1080");
                    }
                    case 104:{
                        return new StringObject(vm, "");
                    }
                    case 102:{
                        return new StringObject(vm, "17637");
                    }
                    case 105:{
                        return new StringObject(vm, "WIFI");
                    }
                    case 106:{
                        return new StringObject(vm, "0.0.0.0:0");
                    }
                    case 8:{
                        return new StringObject(vm, "0.0.0.0:0");
                    }
                    case 9:{
                        return new StringObject(vm, "");
                    }
                    case 10:{
                        return new StringObject(vm, "00:00:00:00:00:00");
                    }
                    case 107:{
                        return new StringObject(vm, "[full-100]");
                    }
                    case 108:{
                        return new StringObject(vm, "78");
                    }
                }
            }
            case "java/net/NetworkInterface->getByName(Ljava/lang/String;)Ljava/net/NetworkInterface;":{
                String name = varArg.getObjectArg(0).getValue().toString();
                System.out.println("NetworkInterface getName:"+name);
                return vm.resolveClass("java/net/NetworkInterface").newObject(name);
            }
            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;":{
                return vm.resolveClass("android/app/ActivityThread").newObject(null);
            }
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "java/io/File->getPath()Ljava/lang/String;":{
                return new StringObject(vm, dvmObject.getValue().toString());
            }
            case "android/content/Context->getPackageResourcePath()Ljava/lang/String;": {
                return new StringObject(vm, "/data/app/ctrip.android.view-fM4xyjk_eygpJsiITNW4JA==/base.apk");
            }
            case "android/content/Context->getFilesDir()Ljava/io/File;":{
                return vm.resolveClass("java/io/File").newObject("/data/data/ctrip.android.view/files");
            }
            case "android/content/Context->getAssets()Landroid/content/res/AssetManager;":{
                return new AssetManager(vm, signature);
            }
            case "java/net/NetworkInterface->getHardwareAddress()[B":
                byte[] result = hexStringToByteArray("64BC0C65AA1E");
                return new ByteArray(vm, result);
            case "android/app/ActivityThread->getApplication()Landroid/app/Application;":{
                return vm.resolveClass("android/app/Application", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(signature);
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    /* s must be an even-length string. */
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}
